package spass.geometry;

import java.text.MessageFormat;
import java.text.NumberFormat;
import java.util.Locale;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.math.geometry.Vector3D;

public class Point {

    private final double x;
    private final double y;
    private final double z;

    public Point(double x, double y, double z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public Point add(Point other) {
        return new Point(getX() + other.getX(), getY() + other.getY(), getZ() + other.getZ());
    }

    public Point substract(Point other) {
        return new Point(getX() - other.getX(), getY() - other.getY(), getZ() - other.getZ());
    }

    public double distanceTo(Point other) {
        Point difference = this.substract(other);

        return toVector3D(difference).getNorm();
    }

    public double dotProduct(Point point) {
        return Vector3D.dotProduct(toVector3D(this), toVector3D(point));
    }

    public double getX() {
        return x;
    }

    public double getY() {
        return y;
    }

    public double getZ() {
        return z;
    }

    public Point copy() {
        return new Point(getX(), getY(), getZ());
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Point) {
            Point that = (Point) obj;

            return new EqualsBuilder().append(getX(), that.getX()).append(getY(), that.getY())
                    .append(getZ(), that.getZ()).isEquals();
        }

        return false;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(getX()).append(getY()).append(getZ()).toHashCode();
    }

    @Override
    public String toString() {
        String xString = inUsFormat(getX());
        String yString = inUsFormat(getY());
        String zString = inUsFormat(getZ());

        return MessageFormat.format("x = {0}; y = {1}; z = {2}", xString, yString, zString);
    }

    private Vector3D toVector3D(Point point) {
        return new Vector3D(point.getX(), point.getY(), point.getZ());
    }

    private String inUsFormat(double x) {
        NumberFormat numberFormat = NumberFormat.getInstance(Locale.US);
        return numberFormat.format(x);
    }

    public static Point origin() {
        return new Point(0, 0, 0);
    }

}
